[id].vue 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145
  1. <template>
  2. <div class="admin--page-content">
  3. <form @submit.prevent="handleSubmit" class="admin--form">
  4. <!-- 낚시지역 -->
  5. <table class="admin--form--table">
  6. <colgroup>
  7. <col style="width: 120px;">
  8. <col>
  9. </colgroup>
  10. <tbody>
  11. <tr>
  12. <th>
  13. <div>
  14. 지역명 <span class="admin--required">*</span>
  15. </div>
  16. </th>
  17. <td>
  18. <div class="input--wrap">
  19. <input
  20. v-model="formData.name"
  21. type="text"
  22. class="admin--form-input"
  23. placeholder="예: 부산/울산"
  24. required
  25. />
  26. <p>시도 단위 지역명. 1~20자. 인접 시도 통합 시 슬래시(/) 사용 (예: 충남/세종)</p>
  27. </div>
  28. </td>
  29. </tr>
  30. </tbody>
  31. </table>
  32. <div class="admin--info--box">
  33. <h3>💡 지역 수정 안내</h3>
  34. <ul>
  35. <li>지역 = 회원 가입 시 선호 지역 선택 + 낚시어선/낚시터 분류 기준</li>
  36. <li>중복 등록 차단. 인접 시도 통합 표기 권장 (예: "충남" 대신 "충남/세종")</li>
  37. </ul>
  38. </div>
  39. <div class="admin--inf--box">
  40. <h3>🔄 영향</h3>
  41. <ul>
  42. <li>이 지역에 등록된 낚시어선, 낚시터의 지역 표시 즉시 갱신</li>
  43. <li>이 지역을 선호 지역으로 등록한 회원의 화면 표시 즉시 갱신</li>
  44. <li>외부 시스템(공공 데이터 API) 연동된 낚시어선/낚시터는 동기화 필요</li>
  45. </ul>
  46. </div>
  47. <!-- 버튼 영역 -->
  48. <div class="admin--form-actions">
  49. <button type="button" class="admin--btn" @click="goToDetail">
  50. ← 취소
  51. </button>
  52. <button type="submit" class="admin--btn admin--btn-red ml--auto" :disabled="isSaving">
  53. {{ isSaving ? "저장 중..." : "저장" }}
  54. </button>
  55. </div>
  56. <!-- 성공/에러 메시지 -->
  57. <div v-if="successMessage" class="admin--alert admin--alert-success">
  58. {{ successMessage }}
  59. </div>
  60. <div v-if="errorMessage" class="admin--alert admin--alert-error">
  61. {{ errorMessage }}
  62. </div>
  63. </form>
  64. </div>
  65. </template>
  66. <script setup>
  67. import { ref, onMounted } from "vue";
  68. import { useRoute, useRouter } from "vue-router";
  69. definePageMeta({
  70. layout: "admin",
  71. middleware: ["auth"],
  72. });
  73. const route = useRoute();
  74. const router = useRouter();
  75. const { get, put } = useApi();
  76. const areaId = route.params.id;
  77. const isSaving = ref(false);
  78. const successMessage = ref("");
  79. const errorMessage = ref("");
  80. const formData = ref({
  81. name: "",
  82. });
  83. // 기존 데이터 로드
  84. const loadDetail = async () => {
  85. const { data, error } = await get(`/area/${areaId}`);
  86. if (error || !data?.success) {
  87. errorMessage.value = error?.message || data?.message || "조회에 실패했습니다.";
  88. return;
  89. }
  90. const row = data.data || {};
  91. formData.value = {
  92. name: row.name ?? "",
  93. };
  94. };
  95. // 폼 제출
  96. const handleSubmit = async () => {
  97. successMessage.value = "";
  98. errorMessage.value = "";
  99. const name = formData.value.name.trim();
  100. if (!name) return (errorMessage.value = "지역명을 입력하세요.");
  101. if (name.length > 20) return (errorMessage.value = "지역명은 20자 이내로 입력하세요.");
  102. isSaving.value = true;
  103. try {
  104. const { data, error } = await put(`/area/${areaId}`, { name });
  105. if (error || !data?.success) {
  106. errorMessage.value = error?.message || data?.message || "수정에 실패했습니다.";
  107. } else {
  108. successMessage.value = data.message || "수정되었습니다.";
  109. setTimeout(() => {
  110. router.push(`/site-manager/area/detail/${areaId}`);
  111. }, 800);
  112. }
  113. } catch (e) {
  114. errorMessage.value = "서버 오류가 발생했습니다.";
  115. console.error("Update error:", e);
  116. } finally {
  117. isSaving.value = false;
  118. }
  119. };
  120. // 이동
  121. const goToDetail = () => router.push(`/site-manager/area/detail/${areaId}`);
  122. onMounted(() => {
  123. loadDetail();
  124. });
  125. </script>